package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Dept;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.EmptyUtil;
import com.zzyl.utils.NoProcessing;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.ResourceVo;
import com.zzyl.vo.TreeItemVo;
import com.zzyl.vo.TreeVo;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ResourceServiceImpl implements ResourceService {
    @Autowired
    ResourceMapper resourceMapper;

    @Cacheable(value = "resource_list", key = "#resourceDto.hashCode()")
    @Override/*查询所有*/
    public List<ResourceVo> resourceList(ResourceDto resourceDto) {
        List<Resource> list = resourceMapper.selectList(resourceDto);
        return BeanUtil.copyToList(list, ResourceVo.class);

    }


    //@Cacheable(value = "resource_tree",key = "#resourceDto.parentResourceNo")/*缓存注解*/
    @Override/*树形结构*/
    public TreeVo resourceTreeVo(ResourceDto resourceDto) {
        //获取所有的资源的数结构
        //构建父节点条件:方式1：创建一个对象，给赋初始值（父节点）
        //ResourceDto dto = new ResourceDto();
        ResourceDto dto = ResourceDto.builder().build();/*相当于上面的new*/

        //将资源列表转成树形结构(查询所有)
        List<Resource> list = resourceMapper.selectList(dto);
        if (list == null || list.size() == 0) {
            throw new RuntimeException("功能信息未定义");/*资源信息未定义*/
        }
        //至此父节点的所有菜单都查出来了，接下来，要将这个列表转成Tree
        //所有的数据出来了，发现100001000000000是权限模块的顶级节点
        Resource root = new Resource();/*相当于拿到了根节点*/
        root.setResourceNo(SuperConstant.ROOT_PARENT_ID);/*资源编号赋值*/
        root.setResourceName("智慧养老");

        /*我们最终的目的是为了转成TreeVo,需要先将查询所有的那个集合编程TreeItemVo*/
        List<TreeItemVo> treeItemVos = new ArrayList<>();

        /*需要用到递归方法,上面定义的三个需要当参数传递给递归方法*/
        recursionTreeItem(list, root, treeItemVos);

        TreeVo treeVo = new TreeVo(treeItemVos);
        return treeVo;

    }




    /*递归方法*/

    /**
     * 递归：将列表转成树结构 @param treeItemVos 树的容器 @param root 根节点 @param list 所有的资
     */
    public void recursionTreeItem(List<Resource> list, Resource root, List<TreeItemVo> treeItemVos) {
        /*先找根节点,然后问它有没有儿子*/
        TreeItemVo rootItemVo = TreeItemVo.builder()
                .id(root.getResourceNo())/*拿根的编号*/
                .label(root.getResourceName())/*和根的编号*/
                .build();
        /*问根有没有儿子,看根是不是他们的爸爸,你的爸爸是我吗?*/
        List<Resource> rootChildren = list.stream().filter(resource -> resource.getParentResourceNo().equals(root.getResourceNo()))
                .collect(Collectors.toList());
        /*判断它有没有儿子,不为空就是有,就递归调用自己,为空就是没有,就直接存到treeItemVos容器中*/
        if (!EmptyUtil.isNullOrEmpty(rootChildren)) {
            List<TreeItemVo> childrenList = new ArrayList<>();/*拿集合接着儿子,因为儿子不止一个,再一个个的问还有没有儿子*/
            /* //问儿子，你有儿子吗？,一个个的问需要循环遍历*/
            rootChildren.forEach(item -> {
                recursionTreeItem(list, item, childrenList);/*item代表该儿子为这次递归的爸爸*/
            });
            /*就继续一个个的问看它是否有儿子*/
            rootItemVo.setChildren(childrenList);/*因为rootItemVo里面包含了一个集合可以接孩子的*/
        }
        /*存入容器中*/
        treeItemVos.add(rootItemVo);
    }


    @Caching(/*缓存注解*/
            evict = {
                    @CacheEvict(value = "resource_tree", allEntries = true),
                    @CacheEvict(value = "resource_list", allEntries = true)
            }
    )
    @Override/*新增资源*/
    public Boolean insertRe(ResourceDto resourceDto) {
        Resource re = BeanUtil.toBean(resourceDto, Resource.class);/*这个容器时空的,需要放东西进去*/
        //资源有禁用和启用，如果父资源是禁用，那么子资源也应该禁用
        Resource parentResource = resourceMapper.selectParentResource(re.getParentResourceNo());/*通过父资源编号查找父资源*/
        if (parentResource.getResourceType().equals("r")) {/*按钮没有子集*/
            throw new BaseException(BasicEnum.BUTTON_NOT_HAVE_CHILD);
        }

        re.setDataState(parentResource.getDataState());/*子资源的状态,要和父资源状态一致*/

        // 资源的层级，支持3级
        boolean isIgnore = true;//如果是按钮，就false
        //资源类型：s平台 c目录 m菜单 r按钮"
        if (StringUtils.isNotEmpty(resourceDto.getResourceType()) && resourceDto.getResourceType().equals("r")) {/*类型不为空且类型是r,就是按钮*/
            isIgnore = false;
        }

        String resourceNo = creatResourceNo(re.getParentResourceNo(), isIgnore);/*获取编号*/
        re.setResourceNo(resourceNo);/*设置编号*/
        resourceMapper.insertRe(re);/*将容器里的东西放到数据库里*/

        return true;
    }


    @Caching(/*缓存注解*/
            evict = {
                    @CacheEvict(value = "resource_tree", allEntries = true),
                    @CacheEvict(value = "resource_list", allEntries = true)
            }
    )
    /*生成资源编号的方法*/
    /**创建资源编号 @param parentResourceNo 父亲编号 @param isIgnore 按钮？*/
    public String creatResourceNo(String parentResourceNo, boolean isIgnore) {

        // 菜单不能大于三级，可以通过父亲的编号来判断，如果父亲已经是三级了，就不能再加了
//* 平台 100 000 000 000 000-->100--1
//* 第1层：100 001 000 000 000-->100 001---2         /*智慧养老*/
//* 第2层：100 001 001 000 000-->100 001 001--3     /*第二层*/
//* 第3层：100 001 001 001 000-->100 001 001 001--4  /*第3层*/
//* 第4层：100 001 001 001 001 001-->100 001 001 001 001--5  /*第4层*/按钮

        if (isIgnore && (NoProcessing.processString(parentResourceNo).length() / 3) >= 4) {
            throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
        }
        //现在有了父亲的编号
        ResourceDto parentResourceDto = ResourceDto.builder().parentResourceNo(parentResourceNo).build();

        List<Resource> childrenReresourcesList = resourceMapper.selectList(parentResourceDto);

        if (EmptyUtil.isNullOrEmpty(childrenReresourcesList)) {
            // 如果父亲没有儿子，我就是老大
            return NoProcessing.createNo(parentResourceNo, false);
        } else {
            // 有儿子，找到儿子中，最大的编号
            Long maxNo = childrenReresourcesList.stream().map(item -> {
                return Long.valueOf(item.getResourceNo());
            }).max(Comparator.comparing(i -> i)).get();
            return NoProcessing.createNo(String.valueOf(maxNo), true);
        }
    }


    @Caching(/*缓存注解*/
            evict = {
                    @CacheEvict(value = "resource_tree", allEntries = true),
                    @CacheEvict(value = "resource_list", allEntries = true)
            }
    )
    @Override/*启用禁用*/
    public Object isEnable(ResourceDto resourceDto) {

        //1.启用资源
        if (resourceDto.getDataState().equals("0")) {
            //1.1 判断父级菜单是否是禁用，如果是禁用，不允许启用
            String parentresourceNo = resourceDto.getParentResourceNo();
            Resource resourcParent = resourceMapper.selectByDeptNo(parentresourceNo);
            if (resourcParent != null && resourcParent.getDataState().equals("1")) {
                throw new BaseException(BasicEnum.PARENT_DEPT_DISABLE);
            }
        }

        //TODO 如果部门下有用户，则不允许禁用

        //判断是否有子菜单，如果有子菜单，则一起启用或禁用
        String resourceNo = resourceDto.getResourceNo();
        resourceMapper.updateByNo(resourceNo, resourceDto.getDataState());/*没有儿子的话就改变自己的状态*/
        resourceMapper.updateByParentNo(NoProcessing.processString(resourceNo), resourceDto.getDataState());/*有儿子就和父亲一起启用禁用*/
        /*NoProcessing.processString(resourceNo)这个方法是用来处理编号后面的零的,三个为一组*/
        return true;
    }

    @Caching(/*缓存注解*/
            evict = {
                    @CacheEvict(value = "resource_tree", allEntries = true),
                    @CacheEvict(value = "resource_list", allEntries = true)
            }
    )
    @Override/*修改资源*/
    public Boolean updateResource(ResourceDto resourceDto) {
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        resourceMapper.updateResource(resource);
        return true;
    }

}
